#include <QLabel>
#include <QGridLayout>
#include <QHeaderView>
#include <QTime>

#include "swcommonwidget.h"

SWCommonWidget::SWCommonWidget()
{
    /* Monospace fonts make looking at rows of bytes a lot easier. :) */
    m_monospaceFont = QFont("Courier New");

    /* How many bytes/chars are written per line. */
    m_wrapOutputAt = 10;

    createWidgets();
    addHiddenExpandEvent();
    setConnected(false);

    /* This makes it so that the destructor of the parent class will be called
       when the widget is closed. This is done to ensure that the server/client
       is properly closed. */
    setAttribute(Qt::WA_DeleteOnClose);
}

void SWCommonWidget::createWidgets()
{
    QGridLayout *layout = new QGridLayout;
    QLabel *dataLabel = new QLabel(tr("Data"));
    m_statusBar = new QStatusBar;

    m_eventTree = new QTreeWidget;
    m_eventTree->setHeaderLabels(QStringList()
            << "Event" << "Time" << "Data (bytes)" << "Data (ascii)");
    /* This is to help prevent the horizontal scrollbar from showing when the
       window first comes up. */
    m_eventTree->header()->setSectionResizeMode(QHeaderView::Stretch);

    m_outputButton = new QPushButton(tr("Send"));
    m_sendLine = new QLineEdit;

    layout->addWidget(dataLabel, 0, 0);
    layout->addWidget(m_sendLine, 0, 1);
    layout->addWidget(m_outputButton, 0, 2);
    layout->addWidget(m_eventTree, 1, 0, 1, 3);
    layout->addWidget(m_statusBar, 3, 0, 1, 3);

    setLayout(layout);
}

/* This adds a fake item that will be as wide as the widest item. This is so
   that the window will start off with the tree widget stretched as much as it
   needs to be. This is also to prevent a silly horizontal scrollbar at the
   bottom of the tree widget. This event is hidden so the user never sees it. */
void SWCommonWidget::addHiddenExpandEvent()
{
    QTreeWidgetItem *hiddenItem = new QTreeWidgetItem;
    QString timestamp = QTime::currentTime().toString("hh:mm:ss AP");
    QString asciiStr = QString("0").repeated(m_wrapOutputAt);
    QString byteStr = QString("000 ").repeated(m_wrapOutputAt);
    int i, newTreeSize;

    /* This is the largest event string for max expansion. */
    hiddenItem->setText(0, "Client Disconnected");
    /* Just using spaces doesn't work because the treewidget thinks it doesn't
       have to do anything. */
    hiddenItem->setText(1, timestamp);
    hiddenItem->setText(2, byteStr);
    hiddenItem->setText(3, asciiStr);

    newTreeSize = 0;
    for (i = 0;i < 4;i++)
        hiddenItem->setFont(i, m_monospaceFont);

    m_eventTree->addTopLevelItem(hiddenItem);

    for (i = 0;i < 4;i++) {
        m_eventTree->resizeColumnToContents(i);
        newTreeSize += m_eventTree->columnWidth(i);
    }

    m_eventTree->setMinimumWidth(newTreeSize);
    hiddenItem->setHidden(true);
}

void SWCommonWidget::addEvent(QString eventName, QString hex, QString ascii)
{
    QTreeWidgetItem *newItem = new QTreeWidgetItem;
    QString timestamp = QTime::currentTime().toString("hh:mm:ss AP");
    newItem->setText(0, eventName);
    newItem->setText(1, timestamp);
    newItem->setText(2, hex);
    newItem->setText(3, ascii);

    int i;
    for (i = 0;i < 4;i++)
        newItem->setFont(i, m_monospaceFont);

    m_eventTree->addTopLevelItem(newItem);
}

void SWCommonWidget::setConnected(bool connected)
{
    m_sendLine->setEnabled(connected);
    m_outputButton->setEnabled(connected);
    if (connected)
        m_sendLine->setFocus();
}

QByteArray SWCommonWidget::escapeText(QString inputStr)
{
    m_isValidEscape = true;
    QByteArray input = inputStr.toUtf8();
    QByteArray output;

    bool dummy;
    int escapeCode, escapeStart, i, inChar, j;
    QString escapeDigits;
    for (i = 0;i < input.length();i++) {
        inChar = input[i];
        if (inChar == '\\') {
            escapeStart = i;
            i++;
            inChar = input[i];
            if (inChar >= '0' && inChar <= '9') {
                escapeDigits = "";
                for (j = 0;j < 3;j++,i++) {
                    if (i == input.length())
                        break;
                    inChar = input[i];
                    if (inChar >= '0' && inChar <= '9')
                        escapeDigits.append(inChar);
                    else
                        break;
                }
                escapeCode = escapeDigits.toInt(&dummy, 10);
                if (escapeCode > 255 || j == 0) {
                    if (j == 0)
                        j = 1;

                    m_isValidEscape = false;
                    m_sendLine->setSelection(escapeStart, escapeStart+j+1);
                    return "";
                }
                i--;
            }
            else {
                if (inChar == '\\' || inChar == '\0')
                    escapeCode = '\\';
                else if (inChar == '"')
                    escapeCode = '"';
                else if (inChar == 'a')
                    escapeCode = '\a';
                else if (inChar == 'b')
                    escapeCode = '\b';
                else if (inChar == 'f')
                    escapeCode = '\f';
                else if (inChar == 'n')
                    escapeCode = '\n';
                else if (inChar == 'r')
                    escapeCode = '\r';
                else if (inChar == 't')
                    escapeCode = '\t';
                else if (inChar == 'v')
                    escapeCode = '\v';
                else if (inChar == 'n')
                    escapeCode = '\n';
                else {
                    m_isValidEscape = false;
                    m_sendLine->setSelection(escapeStart, escapeStart+2);
                    return "";
                }
            }

            output.append(escapeCode);

            if (i == input.length())
                break;
        }
        else
            output.append(inChar);
    }
    return output;
}

QString SWCommonWidget::byteFormat(QByteArray asciiBytes)
{
    /* This is necessary because QString might group bytes together thinking
       they are utf encoded or something. */
    int asciiSize = asciiBytes.size();
    QString ret;
    int i, j;

    for (i = 0, j = 0;i < asciiSize;i++,j++) {
        if (j == m_wrapOutputAt) {
            ret.append("\n");
            j = 0;
        }

        char c = asciiBytes.at(i);
        /* First, a cast to unsigned char so that it will be spread from 0 to
           255. Then cast to int so the right .arg overload is picked.
           3: How much padding to use.
           10: Base 10. */
        ret.append(QString("%1 ").arg((int)(unsigned char)c, 3, 10, QChar('0')));
    }

    return ret;
}

/* Same general idea as above, except showing the printable characters where
   possible. */
QString SWCommonWidget::textFormat(QByteArray asciiBytes)
{
    /* This is necessary because QString might group bytes together thinking
       they are utf encoded or something. */
    int asciiSize = asciiBytes.size();
    QString ret;
    int i, j;

    for (i = 0, j = 0;i < asciiSize;i++,j++) {
        if (j == m_wrapOutputAt) {
            ret.append("\n");
            j = 0;
        }

        char c = asciiBytes.at(i);
        if (isprint(c) == 0)
            c = '.';

        ret.append(c);
    }

    return ret;
}
